<?php
namespace Controller;

use Controller\CsvRowdataBuilder;
/**
 * 处理导出类
 */

final class CsvExport
    {
        private $id       = null;   //记录查询信息的记录id
        private $mgInfo   = null;   //查询记录
        private $savePath = null;   //保存路径
        private $db       = null;
        private $pageSize = 10000;  //第个文件记录数
        private $table    = '';     //查询异动的表名
        private $mgdb     = null;
        private $errorMsg = '';
        private $dataPackate = null;
        private $where = '1';

        function __construct($id, $dataPackate, $db = null)
        {
            if (empty($id)) {
                die('id false');
            }

            $this->db = is_null($db) ? new \MySql() : $db;
            $this->mgdb = new \mgdb();

            $this->id    = $id;
            $this->_init();

            if (!isset($this->mgInfo['table'])) {
                die('table not false');
            }

            $this->table = $this->mgInfo['table'];

            if (empty($dataPackate[$this->table])) {
                die('data package not found');
            }

            $this->dataPackage = $dataPackate[$this->table];
        }

        private function _init()
        {
            $this->mgInfo = $this->getMgdbInfo();
        }

        /**
         * 获取数据
         * @param  [type] $db       [description]
         * @param  [type] $i        [description]
         * @param  [type] $pageSize [description]
         * @param  array  $where    [description]
         * @return [type]           [description]
         */
        function getData($i)
        {
            $db = new \MySql();
            $sql =  "SELECT * FROM {$this->table} WHERE {$this->where} limit " . ($i - 1) * $this->pageSize . ',' . $this->pageSize;

            $list = $db->getAll($sql);

            return $list;
        }

        /**
         * 获取查询记录信息
         * @return [type] [description]
         */
        public function getMgdbInfo()
        {
            //获取记录信息
            $info = $this->mgdb->where(array('_id' => new \MongoId($this->id)))->get('download');

            if (empty($info)) {
                return false;
            }

            $info = current($info);

            return $info;
        }

        /**
         * 记录总数
         * @param  integer $where [description]
         * @return [type]         [description]
         */
        function getTotal()
        {
            $db = new \MySql();
            $total = $db->count($this->table, $this->where);

            return $total;
        }

        /**
         * 创建并返回保存目录
         * @param  [type] $eid [description]
         * @return [type]      [description]
         */
        function getSavePath($eid)
        {
            //$dir = ROOT_PATH .'uploads';
            $savePath = UPLOADPATH . DS . $eid . '_' . $this->id;

            if (!is_dir($savePath)) {
                if (false == mkdir($savePath, "755", true)){
                    $this->errorMsg = '';
                    return false;
                }
            }

            return $savePath;
        }

        /**
         * 执行导出
         * @param  [type] $pageSize [description]
         * @return [type]           [description]
         */
        public function run($pageSize = null)
        {
            if (empty($this->mgInfo)) {
                $this->errorMsg = '没有获取到发起下载记录';
                return false;
            }

            //$this->_createSearchBuilder();  //查询条件

            $this->pageSize = empty($pageSize) ? $this->pageSize : $pageSize; //每个文件多少条记录
            $total = $this->getTotal();    //总符合记录数

            $this->loopNum = ceil($total/$this->pageSize);

            $this->savePath = $this->getSavePath($this->mgInfo['eid']);  //保存路径

            if (empty($this->savePath)) {
                return false;
            }

            $this->tarPath = dirname($savePath);  //打包路径

            //导出
            $exportRes = $this->doExport();

            $tarFilename = $this->zipAcFile($this->mgInfo['eid']);

            //$url = $this->saveTotfs($tarFilename);

            //修改可下载状态
            //$updRes = $this->updDownloadStatus($url);

            //$res = $this->sendSms($this->mgInfo['phone']);

            return true;
        }

        /**
         * 导出操作
         * @return [type] [description]
         */
        public function doExport()
        {
            $i = 0;

            //按每页记录数创进程数
            $pids = []; 
            while ($i < $this->loopNum) {
                $pid = pcntl_fork();

                if ($pid == -1) {
                    break;
                }else{
                    if ($pid > 0) {
                        echo "parent continue,  pid :". getmypid(). ' '.date('Y-m-d H:i:s').PHP_EOL;
                        $pids[] = $pid;
                    }else{
                        echo "child start,  pid :". getmypid(). ' '.date('Y-m-d H:i:s').PHP_EOL;
                        $list = $this->getData($i + 1);

                        if (empty($list)) {
                            exit(0);
                        }

                        $this->saveToCsv($list, $i);

                        exit(0);
                    }
                }

                $i++;

                sleep(10);  //避免数据库连接超时
            }

            while(count($pids) > 0){
                $myId = pcntl_waitpid(-1, $status, WNOHANG);

                foreach ($pids as $k => $pid) {
                    if ($myId == $pid) {
                        unset($pids[$k]);
                    }
                }
            }

            return true;
        }

        /**
         * 保存cvs文件
         * @param  [type] $list [数据]
         * @param  [type] $i    [第几个文件]
         * @return [type]       [description]
         */
        function saveToCsv($list, $i)
        {
            $seperator = '^';
            //$enclosure = "";

            $startNum = ($i * $this->pageSize) + 1;
            $endNum = $startNum + $this->pageSize - 1;

            $fileName =  date('Ymd').'_' . $startNum. '_' . $endNum.'.xls';

            if (file_exists($fileName)) {
                @unlink($this->savePath.DS.$fileName);
            }

            $fp = fopen($this->savePath.DS.$fileName, 'a');

            $columns = $this->dataPackage['header'];
            mb_convert_variables('GBK', 'UTF-8', $columns);

            fputcsv($fp, $columns, $seperator);

            foreach ($list as $k => $v) {
                $bulider = CsvRowdataBuilder::getInstance();
                $rowData = $bulider->buildData($v, $this->table);

                mb_convert_variables('GBK', 'UTF-8', $rowData);

                fputcsv($fp, $rowData, $seperator);
                //unset($list[$k]);
            }

            unset($list, $v);
            fclose($fp);

            return true;
        }

        /**
         * 创建目录
         * @param  [type] $filename [description]
         * @return [type]           [description]
         */
        function makedir($filename){
            return (mkdir($filename, 0755));
        }

        //打包文件
        function zipAcFile($eid)
        {
            $tarPath = dirname($this->savePath);

            $path = explode(DS, $this->savePath);
            $finialPath = array_pop($path);

            $datalist = $this->listDir($this->savePath);

            $fileName = $tarPath . DS . $eid . '_' . $this->id . '.zip';

            if(file_exists($fileName)){
                @unlink($fileName);
            }

            //重新生成文件
            $zip = new \ZipArchive();//使用本类，linux需开启zlib，windows需取消php_zip.dll前的注释

            if ($zip->open($fileName, \ZIPARCHIVE::CREATE)!==TRUE) {
                $this->errorMsg = '无法打开文件，或者文件创建失败';
                return false;
            }

            foreach($datalist as $val){
                if(file_exists($val)){
                    $zip->addFile($val, basename($val));//第二个参数是放在压缩包中的文件名称，如果文件可能会有重复，就需要注意一下
                }
            }

            $zip->close();//关闭
            return $fileName;
        }

        /**
         * 遍历目录
         * @param  [type] $dir [description]
         * @return [type]      [description]
         */
        function listDir($dir){
            $result = array();
            if (is_dir($dir)){
                $file_dir = scandir($dir);
                foreach($file_dir as $file){
                    if ($file == '.' || $file == '..'){
                        continue;
                    }
                    elseif (is_dir($dir.$file)){
                        $result = array_merge($result, list_dir($dir.$file.DS));
                    }
                    else{
                        array_push($result, $dir.DS.$file);
                    }
                }
            }
            return $result;
        }

        /**
         * 上传到tfs
         * @param  [type] $fullFilename [打包好的压缩文件绝对路径]
         * @return [type]               [description]
         */
        function saveTotfs($fullFilename)
        {
            $fileObj['file'] = array(
                'tmp_name' => $fullFilename,
                'name' => basename($fullFilename),
            );

            $up = new \upload($fileObj,'');

            $res = $up->run('file');

            if (!$res) {
                exit($up->errmsg());
            }

            $url = $up->getInfo()[0]['saveName'];
            return $url;
        }

        /**
         * 向雇员发送短信
         * @param  [type] $phone [description]
         * @return [type]        [description]
         */
        private function sendSms($phone)
        {
            $sms = new \sms();
            $res = $sms->SendValidateSMS($phone);

            if (!$res) {
                $this->errorMsg = $sms->getError();
                //return ($sms->getError());
                return false;
            }

            return true;
        }

        /**
         * 修改查询信息，标记可下载
         * @param  [type] $url [description]
         * @return [type]      [description]
         */
        function updDownloadStatus($url)
        {
            $where = array(
                '_id' => new \MongoId($this->id),
            );

            $res = $this->mgdb->where($where)->set(array('status' => intval(1),'url' => $url))->update('download');

            return $res;
        }

        /**
         * 查询条件
         * @param  [type] $url [description]
         * @return [type]      [description]
         */
        private function _createSearchBuilder()
        {
            $info = $this->mgInfo;

            switch($this->table){
            case 'pay_account_cash_out':
                $needsParams = array(
                    'co_day_time',
                    'co_day_time2',
                    'co_state'
                );

                foreach ($needsParams as $v) {
                    if (isset($info[$v]) && '' != $info[$v]) {
                        switch($v){
                        case 'co_day_time':
                            $this->where .= " AND co_day_time > '{$info[$v]}'";
                            break;
                        case 'co_day_time2':
                            $this->where .= " AND co_day_time < '{$info[$v]}'";
                            break;
                        case 'co_state':
                            $this->where .= " AND co_state = '{$info[$v]}'";
                            break;
                        }
                    }
                }
                break;
            case 'pay_account_store_tran':
                break;
            case 'pay_account_score_tran':
            case 'pay_account_store_tran':
            case 'pay_account_tang_tran':
                $needsParams = array(
                    'sc_day_time',
                    'sc_day_time2',
                    'phone',
                );

                foreach ($needsParams as $v) {
                    if (isset($info[$v]) && !empty($info[$v])) {
                        switch($v){
                        case 'sc_day_time':
                            $this->where .= " AND sc_day_time > '{$info[$v]}'";
                            break;
                        case 'sc_day_time2':
                            $this->where .= " AND sc_day_time < '{$info[$v]}'";
                            break;
                        }
                    }
                }
            default:
                break;
            }
        }

        public function getError()
        {
            return $this->errorMsg;
        }
    }
